/*! \file */
/// \addtogroup MegaMan
/// \{
#include "Arduino.h"
#include "MegaMan.h"
#include "p2anim.h"
#include "flash_pics.h"
#include "parsefont.h"

unsigned char frameBuffer[FRAME_BUFFER_SIZE];	// This is also being used by the main program

uint16_t serialTemp = 0; // Temporary storage for data to feed out over serial


/*
	This file includes animation and effect functions
*/



/*
	p2animScrollText() makes the following assumptions:
	- We're alone, i.e. when we are scrolling text, noting else is happening
	- We can use the frameBuffer exclusively
	- We scroll from left to right and then return once the whole string has been scrolled out of the screen
	Relies heavily on the nibble manipulation macros in the header file

	REMEMBER: Rightmost nibble in last byte each line is IGNORED
*/
void p2animScrollText(char *textString)
{

uint8_t yOffset = 5;			// The top row of the letters
uint16_t charOffset;
uint8_t charWidth;

//Serial.begin(115200);	// Debug debug
//Serial.println("Starting text scroll...");
updatePeggyDisplay("clear"); // Clear screen

/* some decoration
memset(&frameBuffer[0], 0xFF, 13);
memset(&frameBuffer[13], 0x11, 13);
memset(&frameBuffer[26], 0x77, 13);
memset(&frameBuffer[39], 0x44, 13);
*/


uint8_t letter_column = 0;		// We draw the leftmost column of the first letter first

while(*textString)				// We loop through the text string
	{
		if(abortFlag) return;	// NOTE: This is how we get out if the Xbee has received something interesting

		if(*textString > 64 && *textString < 91) 	//It's a letter
			{
				charOffset = offset[*textString-50];	// Where to find the data in the variable font
				charWidth = width[*textString-50];		// Width of current character
			} else if(*textString > 47 && *textString < 64)	// it's a number
			{
				charOffset = offset[*textString-48];	// Where to find the data in the variable font
				charWidth = width[*textString-48];		// Width of current character
			} else if(*textString == 32)			// It's a space
			{
				charOffset = 217;	// Where to find the data in the variable font
				charWidth = 5;		// Width of current character
			}
		/*
		Serial.print("Display char: ");
		Serial.print(*textString);
		Serial.print(" = ");
		Serial.println(*textString, DEC);
		Serial.print("Character offset: ");
		Serial.println(charOffset);
		Serial.print("width of char: ");
		Serial.println(charWidth);
		Serial.print("Updating row ");
		Serial.println(letter_column);
		*/
		for (uint8_t y = 0; y < 16; y++)		// Drawing out the current column of the letter
			{
				uint8_t posByte = 12 + (y*13);
				uint16_t fontLocation = charOffset + letter_column + (y * chars_array_width);
				uint8_t gradient = pgm_read_byte_near(my_font + fontLocation);

				setLeftN(frameBuffer[posByte + (yOffset * 13)], gradient);	// Left nibble in last byte (which is the last pixel) from font array (max 15 - 4bit)
			}
		leftShiftScreen(yOffset, (yOffset + chars_heigth));	// We shift (scroll) everything left one LED
        delay(scrollDelay);

		letter_column++;
		if(letter_column > charWidth) 	// End of letter, moving on to next
			{
				letter_column = 0;
				textString++;
			}
	}
// We scroll all the text off screen to finnish off
for (uint8_t i = 0; i < 25; i++)
	{
		leftShiftScreen(yOffset, (yOffset + chars_heigth));	// We shift (scroll) everything left one LED
		//downShiftScreen();
		delay(scrollDelay);
	}

}

/*	Shifts part of screen one column to the left - leaving an empty column at the rightmost side.
	Starting from left, dropping leftmost line (column)
*/
void leftShiftScreen(uint8_t yOffsetTop, uint8_t yOffsetBottom)
{
	for (uint8_t x=0; x < 13; x++)		// We do one vertical row at a time until we've done the whole 25 LED screen width - two LEDs per byte
	{
		for (uint8_t y=yOffsetTop; y <= yOffsetBottom; y++)		// Scrolling each pixel from top to bottom for current column
		{
			setLeftN(frameBuffer[x+(y*13)], getRightN(frameBuffer[x+(y*13)]));		// Shifting right nibble to the left
			if (x == 12) setLeftN(frameBuffer[x+y*13], 0);						// Left nibble zero if at last (25th) column
			if(x<12) // The last byte (13) on a row has the right nibble as the first pixel on the next row - leave it alone!
				setRightN(frameBuffer[x+(y*13)], getLeftN(frameBuffer[(x+1)+(y*13)]));	// Setting right nibble from the left on the next pair
		}
	}
// Done, sending to Peggy
sendBuffer(frameBuffer);
}

/*	Shifts part of screen one column to the right - leaving an empty column at the leftmost side.
	Starting from right, dropping rightmost line (column)
*/
void rightShiftScreen(uint8_t yOffsetTop, uint8_t yOffsetBottom)
{
	for (int8_t x=12; x >= 0; x--)    // We do one vertical row at a time until we've done the whole 25 LED screen width - two LEDs per byte
	{
		Serial.print("Shifting right byte ");
		Serial.println(x);
		for (uint8_t y=yOffsetTop; y <= yOffsetBottom; y++)    // Scrolling each pixel from top to bottom for current column
		{
			if(x<12) setRightN(frameBuffer[x+(y*13)], getLeftN(frameBuffer[x+(y*13)]));    // Shifting left nibble to the right unless at 13th byte (which we ignore)
			if(x>0) // Shift the right nibble from byte to the left into the left nibble of the current byte
				setLeftN(frameBuffer[x+(y*13)], getRightN(frameBuffer[(x-1)+(y*13)]));  // Setting right nibble from the left on the next pair
			if (x == 0) setLeftN(frameBuffer[y*13], 0);            // Left nibble zero if at first (0th) column
		}
	}
// Done, sending to Peggy
sendBuffer(frameBuffer);
}

/* Shifts the entire screen up one LED row, leaving an empty row at the bottom */
void upShiftScreen(void)
{
	for (uint8_t x=0; x < 13; x++)
	{
		for (uint8_t y=1; y < 25; y++) // We start at row number 1 (second row, and work our way down)
		{
			frameBuffer[x+((y-1)*13)] = frameBuffer[x+(y*13)]; // Copying bytes up one position
		}
	}
// Done, sending to Peggy
sendBuffer(frameBuffer);
}

/* Shifts the entire screen down LED row, leaving an empty row at the bottom */
void downShiftScreen(void)
{
	for (uint8_t x=0; x < 13; x++)
	{
		for (uint8_t y=23; y !=0; y--) // We start at row number 23 (24th, and work our way up)
		{
			frameBuffer[x+((y+1)*13)] = frameBuffer[x+(y*13)]; // Copying bytes up one position
		}
	}
// Done, sending to Peggy
sendBuffer(frameBuffer);
}

/* 	Takes a pixel pos (0..24, 0..24) and a gradient (0..15) and updates the frameBuffer - but DOES NOT send it
	Needs optimization - extremely inefficient
*/
void putPixelBuffer(uint8_t x, uint8_t y, uint8_t gradient, uint8_t columns_scrolled)
{
    // We need to change from x pixels, to x_byte byte;
    //	Bytes: 	 	 0	  1	   2  ....
    //  Pixels:		0|1  2|3  4|5 ....
    if(x - columns_scrolled < 0) return;			// We're outside the visible screen, skipping...

    int pos;
    if (x % 2 == 0) 								// We have an *even* x coordinate for the pixel - i.e. leftmost in the byte (MSB)
    {
     	pos = (x / 2) + (y * 13);					// Convert pixel pos to byte number (for the row)
     	pos -= columns_scrolled;
     	setLeftN(frameBuffer[pos], gradient);		// Inserting nibble into leftmost part of byte (MACRO)
    } else					 						// We have an odd number of pixels
    {
		x--;										// We subtract one to get the right nibble address (beginning of the byte we need)
		pos = (x / 2) + (y * 13);					// Convert pixel pos to byte number (for the row)
		pos -= columns_scrolled;
		setRightN(frameBuffer[pos], gradient);		// Inserting nibble into rightmost part of byte (MACRO)
    }
}


void p2animRomantic(void)
	{
	imageFromFlash(smiley_plain);
	delay(1500);
	imageFromFlash(smiley_blink);
	delay(250);
	imageFromFlash(smiley_plain);
	delay(1500);
	alterBuffer("fade_in", a_heart);
	delay(250);
	alterBuffer("fade_out", a_heart);
	}

void starField(void) {

	randomSeed(millis());
	float angle[10];       // Angle in radians
	float velocity[10];    // Speed of star
	float step_x[10];      // Per frame increment to x pos
	float step_y[10];      // Per frame increment to y pos

	float pos_x[10];
	float pos_y[10];
	uint8_t brightness[10];

	for (uint8_t i = 0; i < 10; i++) {
		pos_x[i] = 25/2;
		pos_y[i] = 25/2;

		do {
			velocity[i] = random (1,10) /10;		// from 0.1 to 1.2 - make this random
			} while (velocity[i] == 0);

		angle[i] = random(0,359); // Create in degrees - make this random

		// Convert to radians
		angle[i] = (float)((angle[i] * PI) / 180);

		// Pre calculate the incremental steps; this
		// reduces calc time during execution
		// x increment = cosine(angle) * velocity; y increment = sine(angle) * velocity)
		step_x[i] = (float)(cos(angle[i]) * velocity[i]);
		step_y[i] = (float)(sin(angle[i]) * velocity[i]);

		// Integer brightness from 1 to 15
		brightness[i] = 15;//(uint8_t)random(1, 15);
	}

	for (uint8_t iterations = 0; iterations < 30; iterations++) {
		for (uint8_t i=0; i<10; i++) { //NUM_STARS

		// Move x,y by step_x, step_y
		pos_x[i] += step_x[i];
		pos_y[i] += step_y[i];

		if ((pos_x[i] < 0) || (pos_x[i] > 25) ||
			(pos_y[i] < 0) || (pos_y[i] > 25))
				pos_x[i] = pos_y[i] = 25/2;
		putPixel(ceil(pos_x[i]), ceil(pos_y[i]), brightness[i]);
		}
	}

}

/// \}
